package com.qianfeng.smartplatform.service.impl;

//
//                            _ooOoo_  
//                           o8888888o  
//                           88" . "88  
//                           (| -_- |)  
//                            O\ = /O  
//                        ____/`---'\____  
//                      .   ' \\| |// `.  
//                       / \\||| : |||// \  
//                     / _||||| -:- |||||- \  
//                       | | \\\ - /// | |  
//                     | \_| ''\---/'' | |  
//                      \ .-\__ `-` ___/-. /  
//                   ___`. .' /--.--\ `. . __  
//                ."" '< `.___\_<|>_/___.' >'"".  
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |  
//                 \ \ `-. \_ __\ /__ _/ .-` / /  
//         ======`-.____`-.___\_____/___.-`____.-'======  
//                            `=---='  
//  
//         .............................................  
//                  佛祖镇楼                  BUG辟易  
//          佛曰:  
//                  写字楼里写字间，写字间里程序员；  
//                  程序人员写程序，又拿程序换酒钱。  
//                  酒醒只在网上坐，酒醉还来网下眠；  
//                  酒醉酒醒日复日，网上网下年复年。  
//                  但愿老死电脑间，不愿鞠躬老板前；  
//                  奔驰宝马贵者趣，公交自行程序员。  
//                  别人笑我忒疯癫，我笑自己命太贱；  
//  


import com.qianfeng.smartplatform.cache.DefaultRoleCache;
import com.qianfeng.smartplatform.cache.RoleCache;
import com.qianfeng.smartplatform.exceptions.AddDataException;
import com.qianfeng.smartplatform.exceptions.ResultCode;
import com.qianfeng.smartplatform.mapper.UserMapper;
import com.qianfeng.smartplatform.pojo.CheckType;
import com.qianfeng.smartplatform.pojo.DefaultRole;
import com.qianfeng.smartplatform.pojo.Role;
import com.qianfeng.smartplatform.pojo.User;
import com.qianfeng.smartplatform.service.UserService;
import com.qianfeng.smartplatform.utils.MD5Utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.Date;
import java.util.List;

/**
 * Created by Jackiechan on 2021/12/27/17:04
 *
 * @author Jackiechan
 * @version 1.0
 * @since 1.0
 */
@Service
//@EnableAspectJAutoProxy(proxyTargetClass = true) //通过cglib方式创建类的代理对象,而不是接口类型,但是通过接口仍然可以获取到代理对象
@Transactional//事务控制
public class UserServiceImpl implements UserService {

    private UserMapper userMapper;

    private DefaultRoleCache defaultRoleCache;

    private RoleCache roleCache;

    private ApplicationContext context;

    @Autowired
    public void setContext(ApplicationContext context) {
        this.context = context;
    }

    @Autowired
    public void setDefaultRoleCache(DefaultRoleCache defaultRoleCache) {
        this.defaultRoleCache = defaultRoleCache;
    }

    @Autowired
    public void setRoleCache(RoleCache roleCache) {
        this.roleCache = roleCache;
    }

    @Autowired
    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }


    /**
     * 添加用户
     *
     * @param user
     */
    @Override
    public void addUser(User user) {

        //1 用户名允许空吗?
//        if (StringUtils.hasText(user.getUsername())) {//数据不能是null和"" 包括" ",要求至少存在一个有效的字符
//            //返回,用户没有传递必须传递的数据是不是有问题,有问题我们就抛异常,谁愿意处理谁处理,跟我业务没关系,因为我们不负责返回响应结果
//            // 我们要是抛出异常,抛什么?随便一个异常吗? 我们发现我们需要携带数据出去,所以必须使用自定义异常
//            throw new MyBaseException("传递的用户名是空的", ResultCode.USERNAME_NULL);
//        }

//        if (user.isEmpty(CheckType.ADD)) {
//            //一定代表着有数据没有传递
//            throw new MyBaseException("传递的用户名是空的", ResultCode.USERNAME_NULL);
//        }
//2 密码允许空吗?
        //断言,我断言这个user一定符合我们的要求,只要不符合我就扔异常
        Assert.isTrue(!user.isEmpty(CheckType.ADD), () -> {
            throw new AddDataException("必须传递的数据不完整,请检查", ResultCode.USERNAME_NULL);
        });

        //第二步 判断数据是不是已经存在

        //用户名允许重复吗?
        //手机号允许重复吗?
        //邮箱允许重复吗?
        //等等
        //根据用户名或者手机号或者邮箱查询用户
        List currentUser = userMapper.selectByUserNameOrPhoneOrEmail(user.getUsername(), user.getPhone(), user.getEmail());
        //上面的对象必须是空的,不为空就说明数据已经存在
//        if (currentUser != null) {
//            //抛出异常
//        }
        Assert.isTrue(currentUser.size()==0, () -> {
            throw new AddDataException("帐号已经存在", ResultCode.DATA_ALREADY_EXIST);
        });
        // 我们已经能得知的异常能提前处理的就必须要提前处理
        //前端所有的数据都不可信,前端所有的校验都属于无效校验,前端的校验属于可更改操作,服务端必须做校
        //对数据进行操作, 比如密码需要进行"加密",MD SHA 都不是加密, 基于消息摘要算法实现的校验手段(散列值)
        //加密必须能反向解密.   MD sha 有非常大的熵增,数据的丢失   100G的文件--->MD5 32位字符串
        //非加密算法
        //处理密码,将密码改成MD5值
        String password = user.getPassword();//传递的密码
        //String passwordMD5 = DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8));//将指定的数据转成MD5值
        //如果简单粗暴用上面的方式直接计算md5,相同的密码总是会得到相同的值,那么万一数据泄露了,还是很容易被反向得到密码
        //我们需要喂自己袋盐
        //123456sdfsdfsdfsdafasd-->md5
        //123456hfgdjghkjfghjkfghk-->md5
        //生成随机字符串作为盐
        //程序中有真正的随机数吗?程序中的随机数是利用种子计算出来的
        //如果我想得到一个尽量随机的内容怎么办?避免出现重复内容
        //sessionid 必须不重复,熵池,系统噪声-->计算sessionid
        String salt = MD5Utils.getRandomString(15);//生成了一个长度为15的盐
        //至于是拼在什么位置,无所谓,只有一点要求,这边怎么拼的,后面再计算的时候还要怎么拼

        String md5 = MD5Utils.getMd5(password, salt, 1024);
        user.setPassword(md5);
        user.setPwdSalt(salt);
        user.setCreateTime(new Date());
        user.setCreateBy(user.getUsername());
        //数据库是不支持高并发的, 你搞了一堆的错误进去,不占用数据库的io吗?
        //缓存
        //查询了默认的角色id
        //TODO 问题,我们这个用户的默认的角色id 几乎不会变化,也就是每次注册或者是添加什么数据得到的结果总是一样的,有必要每次都查询数据库吗?
//        Long roleId = defaultRoleMapper.selectRoleIdByType("user");
//        //根据角色的id查询到了角色信息
//        //TODO 同理,这个角色变化的频率相对来说也很低
//        Role role = roleMapper.selectRoleById(roleId);
        DefaultRole defaultRole = defaultRoleCache.getValue("user");
        Role role = roleCache.getValue(defaultRole.getRoleId());
        //设置用户的标识
        user.setRemark(role.getRemark());
        userMapper.addUser(user);
        //用户的角色信息没有保存
        //TODO 我们上面通过select key获取到了插入后的用户主键, 也有角色的主键,就可以插入数据到用户的角色表 user_role表,通过事件操作即可
    }

//    @Override
//    public User login(String username, String password) {
//        //我们判断的标准是和用户的数据进行比较,如果一致就通过
//        //用户数据在什么地方?数据库
//        //查询数据库,用什么条件查询? 帐号查询,帐号有唯一索引,密码没有
//        //我们要求必须传递帐号和密码
//        Assert.isTrue(StringUtils.hasText(username)&&StringUtils.hasText(password),()->{
//            throw new QueryDataException("帐号或者密码错误", ResultCode.USERNAME_PASSOERD_ERROR);
//        });
//        //如果查询不到,是返回null还是返回了一个对象但是里面的属性都是null
//        User user = userMapper.selectUserByUserName(username);
//        //只要查询不到就一定是帐号错误
//        Assert.notNull(user,()->{
//            throw new QueryDataException("帐号或者密码错误", ResultCode.USERNAME_PASSOERD_ERROR);
//        });
//        //有结果就要比较密码
//        //但是用户传递的密码是明文,数据库中是md5值,直接比较不能说一定,但是可以说百分百失败
//        //因为MD5不可逆,所以我们只能选择把用户传递的密码再次转成MD5值,然后进行比较,但是转换的时候需要盐
//        //盐在用户数据中,我们需要获取,然后用一开始注册时候生成MD5的操作重新操作一遍
//        String pwdSalt = user.getPwdSalt();//盐
//        String comMd5 = MD5Utils.getMd5(password, pwdSalt, 1024);//计算出用户现在传递的密码的MD5
//        Assert.isTrue(comMd5.equalsIgnoreCase(user.getPassword()),()->{
//            throw new QueryDataException("帐号或者密码错误", ResultCode.USERNAME_PASSOERD_ERROR);
//        });
//        //说明用户传递的账号和密码是对的
//        user.setPassword(null);
//        user.setPwdSalt(null);
//        //发送事件,更新用户的一些登录后的数据
//      //  context.publishEvent(new UserLoginEvent());
//        return user;
//    }

    @Override
    public int updateUserLogin(User user) {
        //判断时间,不判断,因为这个数据不是用户传递的,是我们自己生成的,我们只要确保一定生成就行
        userMapper.upDateUserLogin(user);
        return 0;
    }

    @Override
    public User findUserByUserName(String username) {
        User user = userMapper.selectUserByUserName(username);
        return user;
    }
//    @EventListener
//    public void onEvent(CommandEvent event) {
//
//    }
}
